/************************************************************************************************************************************************************
*                          DESENVOLVEDORES: Paulo Costa; Diogo Oliveira; Iara Fernanda; Mariana Souza
*                          ORIENTADOR e CO-DESENVOLVEDOR: Prof.Dr. Eraldo Pereira Marinho
*
*                                                           ARQUIVO: parser.c
*
************************************************************************************************************************************************************/

/** 
 * Simplified Pascal in the EBNF:
 * 
 * program -> PROGRAM ID ';' block '.'
 * 
 * block -> declarations modules stmtblock  -- NAO EXISTE MAIS PROCEDURE DENTRO DE PROCEDURE
 * 
 * declarations -> 
 *    [CONST ID = constant';' {ID = constant';'}]
 *    [TYPE ID = type';' {ID = type';'}]
 *    [VAR idlist ':' type';' {idlist ':' type';'}]
 *    
 *
 * modules -> {procedure | function}
 * 
 * procedure -> PROCEDURE ID formalparm';' declarations stmtblock ';'  -- NAO EXISTE MAIS PROCEDURE DENTRO DE PROCEDURE
 * 
 * function -> FUNCTION ID formalparm ':' typeid ';'declarations stmtblock';' -- NAO EXISTE MAIS PROCEDURE DENTRO DE PROCEDURE
 * 
 * stmtblock -> BEGIN stmtlist END
 * 
 * stmtlist -> stmt {';' stmt}
 * 
 * stmt -> stmtblock | ifstmt | whilestmt | repstmt | forstmt | gotostmt | casestmt | idstmt
 * 
 * idstmt -> ID
 */

/** further simplifications for today only:
 * 
 * program -> PROGRAM ID ';' block '.'
 * 
 * block -> declarations stmtblock
 * 
 * declarations -> [VAR idlist ':' type';' {idlist ':' typeid';'}]
 *    modules
 *
 * modules -> {procedure | function}
 * 
 * procedure -> PROCEDURE ID formalparm';' block ';'
 * 
 * function -> FUNCTION ID formalparm ':' typeid ';'block';'
 * 
 * stmtblock -> BEGIN stmtlist END
 * 
 * stmtlist -> stmt {';' stmt}
 * 
 * stmt -> stmtblock | ifstmt | whilestmt | repstmt | idstmt | ""
 */

#include <stdio.h>
#include <string.h>

#include "parser.h"
#include "keywords.h"
#include "tokens.h"
#include "lexer.h"
#include "symtab.h"

#define MAXSYMLIST 0x10000
#define MAXIDSIZE 32

int sympos;
int objtype;
int acctype,returnedtype;
int symbol_entry;
int errflag;
int offset;
char symlist[MAXSYMLIST][MAXIDSIZE];
int sympos;
int labelcount=0;

FILE *target;


void
myparser(void)
{
	
	program();
}

/* 
 * program -> PROGRAM ID ';' block '.'
 */
void
program(void){
  if(lookahead == PROGRAM){
    match(PROGRAM); 
    match(ID);  
    match(';');
  }
  	char *file ="asm_file.s";
	/** */printf("#Header assembly\n");/** */
	/** */printf("\t.file\t\" %s \"\n",file);/** */
	/** */printf(".data\n");/** */
  
  block();
  match('.');  
   

    /** */printf("\n_exit:\n");/** */
    /** */printf("\tleave\n");/** */
    /** */printf("\tret\n\n");/** */
  
}

/*
 * block -> declarations stmtblock
 */
void
block(void){
  declarations();stmtblock();
}

extern int symtab_nextentry;


/*
 * declarations -> [VAR idlist ':' type';' {idlist ':' type';'}] modules
 */
void
declarations(void){
  if(lookahead==VAR){
    match(VAR);
    rep_idlist:
    idlist();
    match(':');
    typeid();
    match(';');
    if(lookahead==ID) goto rep_idlist;
  }
  modules();
}

/*
 * modules -> {procedure | function}
 */
void
modules(void){
  while(lookahead == PROCEDURE || lookahead == FUNCTION){
    if(lookahead == PROCEDURE){
      procedure();
    }
    else function();
  }
}

/*
 * procedure -> PROCEDURE ID formalparm';' block ';'
 */
void
procedure(void){
  char symbolproc[32]; 
  match(PROCEDURE);/** symbol type 3: procedure **/
  strcpy(symbolproc,lexeme);
  match(ID);formalparm();match(';');  
  block();
  match(';');
/*  if (symtab_lookup(lexeme)){
	fprintf(stderr, "symbol \%s\" already declared\n",
	symbolproc);
      
   }else{
	symtab_insert(lexeme, 0, 3);
   }
  */ 
}



/*
 * formalparm -> '(' idlist ':' typeid {; idlist ':' typeid}  ')' ';'
 */
void
formalparm(void){
 char symbolfunc[32];
  match('(');
  idlist(); match(':'); typeid();
  while(lookahead == ';'){
    match(';');
    idlist(); match(':'); typeid();
  }
  match(')'); match(';');
}


/*
 * function -> FUNCTION ID formalparm ':' typeid ';'block';'
 */
void
function(void){
  match(FUNCTION);
  match(ID);  
  formalparm();match(':');
  int tmptype = 0;     //typheck de procedime(4);
  match(';');
  block();
  match(';');
/*  if (symtab_lookup(lexeme)){
	fprintf(stderr, "symbol \%s\" already declared\n",
	lexeme);
      
   }else{
	symtab_insert(lexeme, tmptype, 4);
   }*/
}


/*
 * idlist -> ID {','ID}
 */
void
idlist(void){
  /** **/strcpy(symlist[sympos++],lexeme);/** **/
	match(ID);
  while(lookahead==','){
    match(',');
	/** **/strcpy(symlist[sympos++],lexeme);/** **/
    match(ID);
  }
}

/*
 * typeid -> INTEGER | REAL
 */
int
typeid(void){
  int dtype, i;      /** datatype **/  
  switch(lookahead){
    case INTEGER:
      match(dtype=INTEGER);
      return INTEGER;
      break;
    default:
      match(dtype=REAL);
      return REAL;
  }
  
  /** */
  if(objtype == 1){
    
    for (i=0;i<sympos;i++){
      if (symtab_lookup(symlist[i])){
	fprintf(stderr, "symbol \%s\" already declared\n",
	symlist[i]);
      
      }else{
	symtab_insert(symlist[i],dtype, objtype);
      }
    }
  /** */
    
  }
  
  
}

/*
 * idstmt
 */
void
idstmt(void){

  match(ID);
  match(ASSGNMT);
  expr();
}

/*
 * stmtblock -> BEGIN stmtlist END
 */
void
stmtblock(void){
  match(BEGIN);stmtlist();match(END);
}

/*
 * stmtlist -> stmt {';'stmt}
 */
void
stmtlist(void){
  stmt();
  while(lookahead==';'){
    match(';');stmt();
  }
}

/*
 * ifstmt -> IF expression THEN stmt[ELSE stmt]
 */
void
ifstmt(void){
  int lbl,lbl2 = labelcount++;
  
  /** */printf("\n#IF\n");
  match(IF); 
  expression(); 
  /** */printf("\texpr.trad\n");
  match(THEN);
  /** */printf("\tjne .L%d\n", lbl);
  stmt();/** */printf("stmt.trad");
  if(lookahead==ELSE) {
    /** */printf("\tjmp .L%d\n", ++lbl2);
    /** */printf(".L%d:\n", lbl);    
    stmt();/** */printf("\tstmt.trad\n");
  }
  /** */printf(".L%d:\n", lbl2);
}



/*
 * whilestmt -> WHILE DO stmt
 */
void
whilestmt(void){
  match(WHILE);expression();match(DO);
  stmt();
}


/*
 * repstmt -> REPEAT stmtlist UNTIL expression
 */
void
repstmt(void){
  match(REPEAT);
  stmtlist();
  match(UNTIL);
  expression();
}


/*
 * forstmt-> FOR assign [TO|DOWNTO] expression DO stmt
 */
void
forstmt(void){
  match(FOR);
  assign();
  if (lookahead==TO){
    match(TO);
  }
  else{
    match(DOWNTO);
    expression();
    match(DO);
    stmt();
  }
}


/*
 * stmt -> stmtblock | ifstmt | whilestmt | repstmt | idstmt | ""... complementar
 */
void
stmt(void){
  switch(lookahead){
    case BEGIN:
      stmtblock();
      break;
    case IF:
      ifstmt();
      break;
    case WHILE:
      whilestmt();
      break;
    case REPEAT:
      repstmt();
      break;
    case ID:
      idstmt();
      break;
  }
}


/*
 * gotostm -> GOTO UINT
 */

/*
 * void gotostmt(void){
 *    match('GOTO'); match('UINT');
 }
 */


/*
 * isrelationalop -> '=' | '>' | '=' | '>' | '<' | GEQ | LEQ | NEQ
 */
isrelationalop(token_t token){
  switch(token){
    case '=':
    case '>':
    case '<':
    case GEQ:
    case LEQ:
    case NEQ:
      return token;
  }
  return 0;
}


/*
 * isaddop-> '+' |'-' | OR
 */
isaddop(token_t token){
  switch(token){
    case '+':
      return 1;
      break;
    case '-':
      return 1;
      break;
    case OR:
      return 1;
      break;
    default:
      return 0;
  }
  
}


/*
 * ismulop -> '*' | '/' | AND | DIV | MOD
 */
ismulop(token_t token){
  switch(token){
    case '*':
      return 1;
      break;
    case '/':
      return 1;
      break;
    case AND:
      return 1;
      break;
    case DIV:
      return 1;
      break;
    case MOD:
      return 1;
      break;
    default:
      return 0;
      
  }
  
}

/* expression-> expr [isrelationalop expr]*/
void expression(void)
{
  /** an inherited attribute is get from the left side of an assignment if any **/
  /*OBS: se esta inteiro e apareceu ponto flutuante, quando fechar o parenteses tudo deve ser convertido a ponto flutuante. Se estiver pontu flutuante e aparecer inteiro dentro dos parenteses, manten-se normalmente os tipos*/
  
  
  expr();
  if(isrelationalop(lookahead)){ /* VAMOS ADMITIR QUE SEJA POSSIVEL COMPARAR INTEIRO COM PONTO FLUTUANTE*/
    match(lookahead); expr();  /*AQUI TERA QUE SER UMA VARIAVEL LOGICA*/
  }
  /** at this point we get a resulting data type as a synthesized attribute **/
}

/*expr-> [-] term {isaddop term} */
void expr(void)
{
  /** */int isnegate=0, op=0;/** */
  if(lookahead=='-'){
    /** */ isnegate=1;/** */
    
    match('-');
  }
  term();
  /** */ if(isnegate)fprintf(target, "\tnegl %%eax\n");/** */
  while(isaddop(lookahead)){
    /** */fprintf(target, "\tpushl %%eax\n"); op=lookahead;/** */
    match(lookahead); term();
    /** */ fprintf(target, "\t%s %%eax, (%%esp)\n"); /** */
  }
  /** */ if(op) fprintf(target, "\tpopl %%eax\n"); /** */  /*caso não tenha tido operador*/
}

/*
 * term-> factor {ismulop factor}
 */
void
term(void){
  /** */int op=0;/** */
 
  factor();
  
  while(lookahead==OTIMES){
 /** */fprintf(target, "\tpushl %%eax\n"); 		           op=lookahead;/** */
    
    match(lookahead);
    factor();
/** */
    fprintf(target, "\tpopl %%ecx\n");
    fprintf(target, "\tpushl %%eax\n");
    fprintf(target, "\tmovl %%ecx, %%eax\n");
    switch(op){
      case '*':
      fprintf(target, "\timull  (%%esp)\n");
      break;
      case '/':
      fprintf(target, "\tcltd \n");
      fprintf(target, "\tdivl (%%esp)\n");
      break;
    }
    /** */
  }
}


/*
 * factor-> UINT | FLOAT | ID ['=' expr] |'(' exprlist ')'| NOT factor
 */
void
factor(void){  
  switch(lookahead){
    case UINT:
	/** */
	switch(acctype = max(acctype, 1)){
        case 1:
	  fprintf(target, "\tmovl $%d, %%eax\n", lexeme/*(lexeme)*/);
	  break;
	case 2:
	  fprintf(target, "\tmovl $%d,%%eax\n", lexeme);
	  break;
	}
	/** */
      match(UINT);
      break;
    case UFLOAT:
      match(UFLOAT);
      break;
    case ID:
      match(ID);
      if(lookahead=='('){
	match('(');exprlist();match(')');
      }
      break;
    case '(':
      match('('); expression(); match(')');     /* SE RETURNED TYPE FOR LÃ“GICO, O ACCTYPE SO PODE SER LOGICO */
      /** at this point, expression has synthesized an attribute storing resulting data type in the expression. However, the just calculated attribute might be grater than the Left-attribute along the current expression, so that a promotion must accur (tipo int Ã© menos complexo que ponto flutuante) 
       *	ISSO VAI ACOTECER TAMBÃ‰M PARA INTEIRO SEM SINAL, "PROMOCAO DE TIPO" **/
      break;
      
    default:
     
      match(NOT);factor();  /* PRECISA DE PONTO FLUTUANTE, SE APARECER UMA EXPRESSAO ARITMETICA NÃƒO PODE AFIRMAR NADA, SO NO FINAL DO FATOR SABEREMOS SE O TIPO RETORNADO Ã‰ LOGICO*/
  }
}


/*
 * exprlist-> expression {',' expression}
 */
void
exprlist(void){
  expression();
  while(lookahead==','){
    match(',');expression();
  }
}